import { Injectable, Logger } from '@nestjs/common';
import { Cron, CronExpression } from '@nestjs/schedule';
import { PrismaService } from '../prisma/prisma.service';
import { MailerService } from '@nestjs-modules/mailer';
import Bottleneck from 'bottleneck';
import { Plan, Family, user, Prisma } from '@prisma/client';

/**
 * 计划与关联数据的接口定义
 */
interface PlanWithRelations extends Plan {
  familys: Family[];
  user: user;
}

interface UserWithBirthday extends user {
  familys?: Family[];
}

@Injectable()
export class TasksService {
  private readonly logger = new Logger(TasksService.name);
  // 批处理大小，每次处理的最大计划数
  private static readonly BATCH_SIZE = 10;
  // 邮件发送失败后的最大重试次数
  private static readonly EMAIL_RETRY_ATTEMPTS = 3;
  // 批次之间的延迟时间（毫秒）
  private static readonly EMAIL_BATCH_DELAY = 1000;

  // 使用 Bottleneck 限制并发请求
  private readonly limiter = new Bottleneck({
    maxConcurrent: 5, // 最大并发数
    minTime: 333, // 请求间隔（毫秒）
  });

  constructor(
    private prisma: PrismaService,
    private mailerService: MailerService,
  ) {}

  /**
   * 每天早上10点执行的定时任务
   * 检查并发送第二天的疫苗注射提醒邮件
   */
  @Cron(CronExpression.EVERY_DAY_AT_10AM)
  async handleTaskEveryFiveMinutes() {
    try {
      const plans = await this.fetchPendingPlans();
      if (!plans.length) return;

      await this.processPlanBatches(plans);
    } catch (error) {
      this.logger.error('Failed to process plans:', error);
    }
  }

  /**
   * 获取待处理的计划列表
   * 查询明天需要执行且未发送提醒的计划
   */
  private async fetchPendingPlans(): Promise<PlanWithRelations[]> {
    const tomorrow = new Date();
    tomorrow.setDate(tomorrow.getDate() + 1);
    tomorrow.setHours(0, 0, 0, 0);

    return this.prisma.plan.findMany({
      where: {
        is_remind: 1,
        is_done: 0,
        is_send: 0,
        createdAt: {
          gte: tomorrow,
          lt: new Date(tomorrow.getTime() + 24 * 60 * 60 * 1000),
        },
      },
      include: {
        familys: true,
        user: true,
      },
    });
  }

  /**
   * 批量处理计划
   * @param plans 待处理的计划列表
   */
  private async processPlanBatches(plans: PlanWithRelations[]) {
    const batches = this.chunkArray(plans, TasksService.BATCH_SIZE);

    for (const batch of batches) {
      await Promise.all(batch.map((plan) => this.processSinglePlan(plan)));
      await this.delay(TasksService.EMAIL_BATCH_DELAY);
    }
  }

  /**
   * 处理单个计划
   * @param plan 待处理的计划
   */
  private async processSinglePlan(plan: PlanWithRelations) {
    const validFamilyMembers = plan.familys.filter((family) => family.email);
    if (!validFamilyMembers.length) return;

    try {
      await Promise.all(
        validFamilyMembers.map((family) =>
          this.limiter.schedule(() => this.sendEmailWithRetry(plan, family)),
        ),
      );

      await this.updatePlanStatus(plan.id);
    } catch (error) {
      this.logger.error(`Failed to process plan ${plan.id}:`, error);
    }
  }

  /**
   * 发送邮件（带重试机制）
   * @param plan 计划信息
   * @param family 家庭成员信息
   * @param attempt 当前重试次数
   */
  private async sendEmailWithRetry(
    plan: PlanWithRelations,
    family: Family,
    attempt = 1,
  ): Promise<boolean> {
    try {
      await this.mailerService.sendMail({
        to: family.email,
        subject: plan.type ==2 ? '儿保提醒' : '疫苗提醒',
        template: plan.type ==2 ? 'child-care-reminder' : 'plan-reminder',
        context: {
          userName: plan.user?.name || '用户',
          familyName: family.name || '家人', 
          planType: plan.type,
          planDate: plan.type == 2 ? plan.createdAt.toLocaleDateString() : plan.createdAt.toLocaleString(),
          planName: plan.plan_name,
        },
      });
      return true;
    } catch (error) {
      if (attempt < TasksService.EMAIL_RETRY_ATTEMPTS) {
        // 指数退避策略，每次重试的等待时间翻倍
        await this.delay(Math.pow(2, attempt) * 1000);
        return this.sendEmailWithRetry(plan, family, attempt + 1);
      }
      this.logger.error(
        `Failed to send email to ${family.email} after ${TasksService.EMAIL_RETRY_ATTEMPTS} attempts:`,
        error,
      );
      return false;
    }
  }

  /**
   * 更新计划状态
   * @param planId 计划ID
   */
  private async updatePlanStatus(planId: number) {
    try {
      await this.prisma.plan.update({
        where: { id: planId },
        data: { is_send: 1 },
      });
    } catch (error) {
      this.logger.error(`Failed to update plan status for ${planId}:`, error);
    }
  }

  /**
   * 将数组分割成指定大小的小数组
   * @param array 原数组
   * @param size 每个小数组的大小
   */
  private chunkArray<T>(array: T[], size: number): T[][] {
    return Array.from({ length: Math.ceil(array.length / size) }, (_, i) =>
      array.slice(i * size, i * size + size),
    );
  }

  /**
   * 延迟执行
   * @param ms 延迟时间（毫秒）
   */
  private delay(ms: number): Promise<void> {
    return new Promise((resolve) => setTimeout(resolve, ms));
  }

  /**
   * 每天零点执行的生日提醒任务
   */
  @Cron(CronExpression.EVERY_DAY_AT_MIDNIGHT)
  async handleBirthdayReminders() {
    try {
      const birthdayUsers = await this.fetchTodayBirthdays();
      if (!birthdayUsers.length) return;

      await Promise.all(
        birthdayUsers.map((user) => this.sendBirthdayReminder(user)),
      );
    } catch (error) {
      this.logger.error('Failed to process birthday reminders:', error);
    }
  }

  /**
   * 获取今天过生日的用户
   */
  private async fetchTodayBirthdays(): Promise<UserWithBirthday[]> {
    const today = new Date();

    const userIds = await this.prisma.$queryRaw<{ id: number }[]>`
      SELECT id 
      FROM user 
      WHERE 
        MONTH(birthday) = ${today.getMonth() + 1}
        AND DAY(birthday) = ${today.getDate()}
        AND birthday IS NOT NULL
    `;

    return this.prisma.user.findMany({
      where: {
        id: {
          in: userIds.map((u) => u.id),
        },
      },
      include: {
        familys: true,
      },
    });
  }

  /**
   * 发送生日提醒邮件
   */
  private async sendBirthdayReminder(user: UserWithBirthday) {
    if (!user.familys?.length) return;

    const emailPromises = user.familys.map((family) =>
      this.limiter.schedule(() =>
        this.mailerService.sendMail({
          to: family.email,
          subject: '生日提醒',
          template: 'birthday-reminder',
          context: {
            userName: user.name,
            familyName: family.name,
            birthday: user.birthday,
          },
        }),
      ),
    );

    try {
      await Promise.all(emailPromises);
      this.logger.log(
        `Successfully sent birthday reminders for user ${user.id}`,
      );
    } catch (error) {
      this.logger.error(
        `Failed to send birthday reminders for user ${user.id}:`,
        error,
      );
    }
  }
}
